﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using Library;

namespace Directory
{
  /// <summary>
  /// 所属している全グループ画面のフォームです。
  /// </summary>
  internal partial class InRoleGroupList : Form
  {
    #region プライベートフィールド
    private readonly DomainObject member;   //メンバー
    private readonly Dictionary<string, DomainGroup> allGroups;   //全グループ(Key:distinguishedName)
    private readonly List<string> addedDisplayNames;  //リストに追加したグループの表示テキスト
    #endregion

    #region パブリックコンストラクター
    /// <summary>
    /// メンバーを指定して InRoleGroupList クラスの新しいインスタンスを初期化します。
    /// </summary>
    /// <param name="member">メンバー。</param>
    public InRoleGroupList(DomainObject member)
    {
      InitializeComponent();

      this.member = member;
      allGroups = DirectoryAccess.GetGroups<DomainGroup>().ToDictionary(
        group => group.Entry.Properties["distinguishedName"].Value.ToString(), group => group);
      addedDisplayNames = new List<string>();

      this.AddChildNode();  //子ノードを追加
      this.GroupListBox.DataSource = addedDisplayNames;
      this.ViewPanel.Enabled = addedDisplayNames.Count > 1;
      this.GroupTreeView.ExpandAll();
    }
    #endregion

    #region イベントハンドラ
    //TreeRadioButton, ListRadioButton
    private void RadioButton_CheckedChanged(object sender, EventArgs e)
    {
      this.GroupTreeView.Visible = sender == this.TreeRadioButton;
      this.GroupListBox.Visible = !this.GroupTreeView.Visible;
    }
    #endregion

    #region プライベートメソッド
    /// <summary>
    /// 子ノードを追加します。
    /// </summary>
    private void AddChildNode()
    {
      var imageIndex = (int)member.Category;
      var node = new TreeNode(member.ToString(), imageIndex, imageIndex);
      this.GroupTreeView.Nodes.Add(node);
      if ((member.Category == CategoryType.User) || (member.Category == CategoryType.Computer))
      {
        //プライマリー グループを追加
        var primaryGroupId = (member.Category == CategoryType.User) ?
          ((DomainUser)member).PrimaryGroupId : ((Computer)member).PrimaryGroupId;
        var group = allGroups.Values.Cast<DomainGroup>().Single(grp => grp.Token == primaryGroupId);
        this.AddChildNode(node, group);   //子ノードを追加
      }
      //所属するグループを追加
      foreach (string memberOf in member.Entry.Properties["memberOf"])
      {
        this.AddChildNode(node, allGroups[memberOf]);   //子ノードを追加
      }
    }

    /// <summary>
    /// 子ノードを追加します。
    /// </summary>
    /// <param name="node">子ノードを追加するノード。</param>
    /// <param name="group">子ノードになるグループ。</param>
    private void AddChildNode(TreeNode node, DomainGroup group)
    {
      var childNode = CreateNode(group);  //ノードを作成
      node.Nodes.Add(childNode);
      if (addedDisplayNames.Contains(childNode.Text) == false)
      {
        addedDisplayNames.Add(childNode.Text);  //表示するテキストを追加
      }
      //ネストしているグループを追加
      foreach (string memberOf in group.Entry.Properties["memberOf"])
      {
        this.AddChildNode(childNode, allGroups[memberOf]);  //子ノードを追加
      }
    }

    /// <summary>
    /// 指定したグループのノードを作成します。
    /// </summary>
    /// <param name="group">グループ。</param>
    /// <returns>作成したノード。</returns>
    private TreeNode CreateNode(DomainGroup group)
    {
      var displayName = String.Format("{0}（{1}）", group.Name, group.Scope);   //表示するテキスト
      var fontColor = Color.Black;
      switch (group.ScopeType)
      {
        case DomainGroupScopeType.DomainLocal:
          fontColor = Color.Blue;
          break;
        case DomainGroupScopeType.Global:
          fontColor = Color.Green;
          break;
        case DomainGroupScopeType.Universal:
          fontColor = Color.Brown;
          break;
      }

      return new TreeNode(displayName) { Name = group.Name, ForeColor = fontColor };
    }
    #endregion
  }
}
